package org.vnl.platform.impl.orm;

import java.sql.Connection;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.vnl.platform.orm.Command;
import org.vnl.platform.orm.CommandParameter;
import org.vnl.platform.orm.IConnectionManager;
import org.vnl.platform.orm.IPersistenceManager;
import org.vnl.platform.orm.ORMEnvCode;
import org.vnl.platform.orm.PageResult;
import org.vnl.platform.orm.QueryCommand;
import org.vnl.platform.orm.TransactionErrCode;
import org.vnl.platform.orm.TransactionException;


public class DefaultPersistenceManager implements IPersistenceManager {
    private static final Logger logger = Logger.getLogger(DefaultPersistenceManager.class);
    
    private IConnectionManager cm;
    
    private ThreadLocal<Boolean> inTxn;

    private ThreadLocal<Boolean> rollbackOnly;
    
    public DefaultPersistenceManager() {
        inTxn = new ThreadLocal<Boolean>();
        rollbackOnly = new ThreadLocal<Boolean>();
    }
    
    @Override
    public void begin() {
        if (inTxn.get() != null) {
            if (inTxn.get()) {
                return;
            }
        }
        Connection conn = cm.getCurrentConnection();
        try {
            conn.setAutoCommit(false);
        } catch (SQLException e) {
        }
        inTxn.set(new Boolean(true));
        rollbackOnly.set(false);
    }

    @Override
    public void commit() {
        if (isRollbackOnly()) {
            throw new TransactionException(TransactionErrCode.TRANSACTION_MUST_ROLLBACK);
        }
        if (inTxn.get() != null) {
            if (inTxn.get()) {
                Connection conn = cm.getCurrentConnection();
                try {
                    if (!conn.getAutoCommit()) {
                        conn.commit();
                    }
                } catch (SQLException e) {
                    logger.error("Begin transaction error!",e);
                    throw new TransactionException(TransactionErrCode.ERR_SQL_EXCEPTION, new Object[] {e.getMessage()}, e);
                } finally {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        logger.fatal("Cannot close connection");
                    }
                }
            }
        }
        logger.info("Need not to commit as transaction has not been started.");
    }

    @Override
    public void rollback() {
        if (inTxn.get() != null) {
            if (inTxn.get()) {
                Connection conn = cm.getCurrentConnection();
                try {
                    if (!conn.getAutoCommit()) {
                        conn.rollback();
                    }
                } catch (SQLException e) {
                    logger.error("Begin transaction error!",e);
                    throw new TransactionException(TransactionErrCode.ERR_SQL_EXCEPTION, new Object[] {e.getMessage()}, e);
                } finally {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        logger.fatal("Cannot close connection");
                    }
                }
            }
        }
        logger.info("Need not to rollback as transaction has not been started.");
        
    }

    @Override
    public void save(Command cmd) {
        String tid = cmd.getName();
        if (StringUtils.isBlank(tid)) {
            throw new TransactionException(ORMEnvCode.ERR_INCORRECT_IMPLEMENTATION, new Object[] { "Transaction ID missing!" });
        }
        PreparedStatement stmt = null;
        try {
            stmt = getSQLStatement(tid, cmd.getCondition());
            stmt.execute();
        } catch (Exception e) {
            logger.error("Execute SQL error:"+e.getMessage(),e);
            throw new TransactionException(TransactionErrCode.ERR_SQL_EXCEPTION, new Object[] { e.getMessage() }, e);
        } finally {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    logger.fatal("Cannot close statement",e);
                }
            }
        }
        
    }

    @Override
    public void create(Command obj) {
        save(obj);
    }

    @Override
    public void update(Command obj) {
        update(obj);
    }

    @Override
    public Collection<Object> query(QueryCommand cmd) {
        return null;
    }

    @Override
    public void setConnectionManager(IConnectionManager cm) {
        this.cm = cm;
    }

    @Override
    public void delete(Command obj) {
        save(obj);
    }
    
    private PreparedStatement getSQLStatement(String tid, List<CommandParameter> params) throws SQLException {
        Connection conn = cm.getCurrentConnection();
        conn.prepareStatement(TransactionDescriptor.getInstance().getSQL(tid));
        
        return null;
    }

    @Override
    public Object getByKey(QueryCommand key) {
        
        return null;
    }

    @Override
    public PageResult queryPage(int currentPage, int pageSize, QueryCommand obj) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isRollbackOnly() {
        Boolean obj = this.rollbackOnly.get();
        if (obj == null) {
            return false;
        }
        return obj.booleanValue();
    }

    @Override
    public void setRollbackOnly(boolean rollbackOnly) {
        this.rollbackOnly.set(rollbackOnly);
    }

}
